package com.gitee.wsl.doc.prosemirror.history.ropesequence

import kotlin.math.max
import kotlin.math.min

class Append<T>(
    val left: RopeSequence<T>,
    val right: RopeSequence<T>
) : RopeSequence<T>() {
    override val length = left.length + right.length
    override val depth = max(left.depth, right.depth) + 1

    override fun flatten(): List<T> {
        return this.left.flatten() + this.right.flatten()
    }

    override fun getInner(i: Int): T? {
        return if (i < this.left.length) this.left.get(i) else this.right.get(i - this.left.length)
    }

    @Suppress("ReturnCount")
    override fun forEachInner(from: Int, to: Int, start: Int, f: (T, Int) -> Boolean): Boolean {
        val leftLen = this.left.length
        if (from < leftLen && !this.left.forEachInner(from, min(to, leftLen), start, f)) {
            return false
        }
        if (to > leftLen &&
            !this.right.forEachInner(
                max(from - leftLen, 0),
                min(this.length, to) - leftLen,
                start + leftLen,
                f
            )
        ) {
            return false
        }
        return true
    }

    @Suppress("ReturnCount")
    override fun forEachInvertedInner(from: Int, to: Int, start: Int, f: (T, Int) -> Boolean): Boolean {
        val leftLen = this.left.length
        if (from > leftLen &&
            !this.right.forEachInvertedInner(from - leftLen, max(to, leftLen) - leftLen, start + leftLen, f)
        ) {
            return false
        }
        if (to < leftLen && !this.left.forEachInvertedInner(min(from, leftLen), to, start, f)) {
            return false
        }
        return true
    }

    @Suppress("ReturnCount")
    override fun sliceInner(from: Int, to: Int): RopeSequence<T> {
        if (from == 0 && to == this.length) return this
        val leftLen = this.left.length
        if (to <= leftLen) return this.left.slice(from, to)
        if (from >= leftLen) return this.right.slice(from - leftLen, to - leftLen)
        return this.left.slice(from, leftLen).append(this.right.slice(0, to - leftLen))
    }

    override fun leafAppend(other: RopeSequence<T>): Append<T>? {
        val inner = this.right.leafAppend(other)
        return if (inner != null) {
            Append(this.left, inner)
        } else {
            null
        }
    }

    override fun leafPrepend(other: RopeSequence<T>): Append<T>? {
        val inner = this.left.leafPrepend(other)
        return if (inner != null) {
            Append(inner, this.right)
        } else {
            null
        }
    }

    override fun appendInner(other: RopeSequence<T>): Append<T> {
        if (this.left.depth >= max(this.right.depth, other.depth) + 1) {
            return Append(this.left, Append(this.right, other))
        }
        return Append(this, other)
    }

    override fun toString(): String {
        return "Append {left: ${left::class.simpleName}, right: ${right::class.simpleName}, " +
            "lenght: $length, depth: $depth}"
    }
}